package client

import (
	"chess_platform/internal/alisdk"
	. "chess_platform/internal/common"
	"encoding/json"
	"fmt"
	"github.com/astaxie/beego/orm"
	"strconv"
	"strings"
	"time"
)

//新建登录日志
func NewLoginLog(data map[string]interface{}) (int64, error) {
	o := NewDBOrm(DBChessLog)
	values, sql := InsertSql(TablePlayerLoginLog, data)
	result, err := o.Raw(sql, values).Exec()
	if err != nil {
		return 0, err
	}
	return result.LastInsertId()
}

func NewUser(user map[string]interface{}, multiOrm ...orm.Ormer) (int64, error) {
	o := NewOrm(multiOrm)
	values, sql := InsertSql(TableClientUser, user)
	result, err := o.Raw(sql, values).Exec()
	if err != nil {
		return 0, err
	}
	return result.LastInsertId()
}

func NewPassport(passport map[string]interface{}, multiOrm ...orm.Ormer) (int64, error) {
	o := NewOrm(multiOrm)
	values, sql := InsertSql(TableClientPassport, passport)
	result, err := o.Raw(sql, values).Exec()
	if err != nil {
		return 0, err
	}
	return result.LastInsertId()
}

//更新玩家通行证信息(passport表)
func UpdatePassport(passport map[string]interface{}, uid int64, multiOrm ...orm.Ormer) (int64, error) {
	o := NewOrm(multiOrm)
	condition := fmt.Sprintf(`uid = %v`, uid)
	values, sql := UpdateSql(TableClientPassport, passport, condition)
	result, err := o.Raw(sql, values).Exec()
	if err != nil {
		return 0, err
	}
	return result.RowsAffected()
}

//更新用户信息(user表)
func UpdateUser(updateData map[string]interface{}, uid int64, multiOrm ...orm.Ormer) (int64, error) {
	o := NewOrm(multiOrm)
	condition := fmt.Sprintf(`uid = %v`, uid)
	values, sql := UpdateSql(TableClientUser, updateData, condition)
	result, err := o.Raw(sql, values).Exec()
	if err != nil {
		return 0, err
	}
	return result.RowsAffected()
}

//更新(绑定)玩家的手机号,两个表都要更新(user,passport)
func UpdateUserPhone(phone string, uid int64, multiOrm ...orm.Ormer) (int64, error) {
	data := map[string]interface{}{
		"phone": phone,
	}

	n, err := UpdatePassport(data, uid, multiOrm...)
	if err != nil {
		return n, err
	}
	data["phone_time"] = time.Now().Unix()

	return UpdateUser(data, uid, multiOrm...)
}

func PassportInfoByTelephone(phone string, multiOrm ...orm.Ormer) (*Passport, error) {
	sql := fmt.Sprintf(`
		SELECT
			*
		FROM
			%v
		WHERE
			phone = ?
		LIMIT 1
		`, TableClientPassport)
	o := NewOrm(multiOrm)
	var p Passport
	err := o.Raw(sql, phone).QueryRow(&p)
	if CheckNoExist(err) {
		return nil, nil
	}
	return &p, err
}

func PassportInfoByUid(uid int64, multiOrm ...orm.Ormer) (*Passport, error) {
	sql := fmt.Sprintf(`
		SELECT
			*
		FROM
			%v
		WHERE
			uid = ?
		LIMIT 1
		`, TableClientPassport)
	o := NewOrm(multiOrm)
	var p Passport
	err := o.Raw(sql, uid).QueryRow(&p)
	if CheckNoExist(err) {
		return nil, nil
	}
	return &p, err
}
func PassportInfoByUnionId(unionId string, multiOrm ...orm.Ormer) (*Passport, error) {
	sql := fmt.Sprintf(`
		SELECT
			*
		FROM
			%v
		WHERE
			union_id = ?
		LIMIT 1
		`, TableClientPassport)
	o := NewOrm(multiOrm)
	var p Passport
	err := o.Raw(sql, unionId).QueryRow(&p)
	if CheckNoExist(err) {
		return nil, nil
	}
	return &p, err
}

func IsUserExistByPhone(phone string, multiOrm ...orm.Ormer) (int64, int64, error) {
	sql := fmt.Sprintf(`
		SELECT
			uid,phone_time
		FROM
			%v
		WHERE
			phone = ?
		LIMIT 1
		`, TableClientUser)
	o := NewOrm(multiOrm, DBChess)
	var uid, phoneTime int64
	err := o.Raw(sql, phone).QueryRow(&uid, &phoneTime)
	if CheckNoExist(err) {
		return 0, 0, nil
	}
	return uid, phoneTime, err
}

//获取玩家的信息展示到pc端
func UserInfoById(id int64, multiOrm ...orm.Ormer) (*CtUser, error) {
	sql := fmt.Sprintf(`
		SELECT
			*
		FROM
			%v
		WHERE
			uid = ?
		LIMIT 1
		`, TableClientUser)
	o := NewOrm(multiOrm)
	o.Using(DBChess)
	var user CtUser
	err := o.Raw(sql, id).QueryRow(&user)
	if CheckNoExist(err) {
		return nil, nil
	}
	return &user, err
}

func SpecifyUserInfoById(uid int64, field []string, multiOrm ...orm.Ormer) (*CtUser, error) {
	fieldStr := strings.Join(field, ",")
	sql := fmt.Sprintf(`
		SELECT
			%v
		FROM
			%v
		WHERE
			uid = %v
		LIMIT 1
		`, fieldStr, TableClientUser, uid)
	o := NewOrm(multiOrm)
	var user CtUser
	err := o.Raw(sql).QueryRow(&user)
	if err == orm.ErrNoRows {
		return nil, nil
	}
	return &user, err
}
func UserDiamondById(uid int64, multiOrm ...orm.Ormer) int64 {
	sql := fmt.Sprintf(`
		SELECT
			rmcard
		FROM
			%v
		WHERE
			uid = %v
		LIMIT 1
		`, TableClientUser, uid)
	o := NewOrm(multiOrm)
	var diamond int64
	o.Raw(sql).QueryRow(&diamond)
	return diamond
}

// 查询玩家显示信息
func QueryUserInfoById(uid interface{}, multiOrm ...orm.Ormer) (UserFace, error) {
	var user UserFace
	key := fmt.Sprintf("%v%v", KeyClientUserInfo, uid)
	err := GetCache(RedisCache, key, &user)
	if err != nil { //doesn't exist,get it from mysql
		o := NewOrm(multiOrm, DBChess)
		sql := fmt.Sprintf(`
			SELECT
				uid,nickname,sex,loginip,imageurl,lon,lat,city_code,phone,identity,real_name
			FROM
				%v
			WHERE
				uid = %v
			LIMIT 1`, TableClientUser, uid)
		err = o.Raw(sql).QueryRow(&user)
		if CheckError(err) {
			return user, err
		}
		if CheckNoExist(err) {
			return user, nil
		}
		user.ImageUrl = handleEmptyHeadImg(user.ImageUrl)
		SetCache(RedisCache, key, user, CacheExpire)
	}
	return user, nil
}

func handleEmptyHeadImg(headImg string) string {
	if headImg != "" {
		return headImg
	}
	return alisdk.GetOssPathUrl(OssDomain) + "/" + PlatformRunMode + "/default_image.png"
}

type PlayerInfo struct {
	Uid       int64  `json:"uid"`
	Nickname  string `json:"nickname"`
	ImageUrl  string `json:"imageurl" orm:"column(imageurl)"`
	LoginTime int64  `json:"logintime" orm:"column(logintime)"`
}

//查询玩家信息
func FindPlayer(uid int64) PlayerInfo {
	sql := fmt.Sprintf(`
		SELECT
			uid,nickname,imageurl,logintime
		FROM
			%v
		WHERE
			uid = %v
	`, TableClientUser, uid)
	o := NewDBOrm(DBChess)
	var pi PlayerInfo
	o.Raw(sql).QueryRow(&pi)
	pi.ImageUrl = handleEmptyHeadImg(pi.ImageUrl)
	return pi

}

//通过传多个玩家id获取多个玩家的信息
func MultiUserInfoById(uid []int64) ([]UserFace, error) {
	var allUser []UserFace
	o := NewDBOrm(DBChess)
	for _, v := range uid {
		user, err := QueryUserInfoById(v, o)
		if err != nil {
			return allUser, err
		}
		if user.Uid == 0 { //信息为空的不能返回，王永利要求的
			continue
		}
		user.Phone = "" //不能暴露其它用户的敏感信息
		user.Identity = ""
		user.RealName = ""
		allUser = append(allUser, user)
	}

	return allUser, nil
}

//获取用户的是否被冻结了各上次登录时间
func IsUserFrozenAndLoginTime(uid int64, multiOrm ...orm.Ormer) (*CtUser, error) {
	sql := fmt.Sprintf(`
		SELECT
			uid,frozen,frozen_reason,logintime
		FROM
			%v
		WHERE
			uid = ?
		LIMIT 1
		`, TableClientUser)
	o := NewOrm(multiOrm)
	var user CtUser
	err := o.Raw(sql, uid).QueryRow(&user)
	if CheckNoExist(err) {
		return &user, nil
	}
	return &user, err
}

type TmpPassport struct {
	AccessToken string `json:"access_token"`
	Uid         int64  `json:"uid"`
}

func IsPassportExistByOpenID(openId string, multiOrm ...orm.Ormer) (TmpPassport, error) {
	t, err := isPassportExistByOpenID(openId, multiOrm...)
	if err == ErrInvalidConn || err == ErrBadConnNoWrite { //连接无效,再尝试一次
		return isPassportExistByOpenID(openId, multiOrm...)
	}
	return t, err
}

func isPassportExistByOpenID(openId string, multiOrm ...orm.Ormer) (TmpPassport, error) {
	var pp TmpPassport
	key := fmt.Sprintf("%v%v", KeyClientOpenId, openId)
	err := GetCache(MemCache, key, &pp)
	if err != nil {
		LogClient.Debug(fmt.Sprintf("open_id:%v缓存不存在", openId))
		sql := fmt.Sprintf(`
		SELECT
			uid,access_token
		FROM
			%v
		WHERE
			open_id = ?
		LIMIT 1
		`, TableClientPassport)
		o := NewOrm(multiOrm)
		err := o.Raw(sql, openId).QueryRow(&pp)
		LogClient.Debug(fmt.Sprintf("open_id:%v从数据库获取的access_token:%#v\n", openId, pp))
		//不存在直接返回，不能让它保存在缓存里，如果缓存里为空就会注册一个帐号，数据库里有记录了，但是缓存里还是为空数据
		//来访问时直接从缓存里取到数据，但是却是空数据，所以以为数据库没记录，所以又会注册一个账号
		if err == orm.ErrNoRows {
			LogClient.Debug(fmt.Sprintf("open_id:%v不存在数据库获里", openId))
			return pp, err
		} else if err != nil {
			LogClient.Debug(fmt.Sprintf("open_id:%v从数据库里查询时发生错误:%v", openId, err))
			return pp, err
		}
		SetCache(MemCache, key, pp, CacheExpire) //一个小时,因为微信的access_token有效期是2个小时
	}
	return pp, nil
}

//读取玩家收取全服邮件的游标,即玩家个人邮件列表里已经有邮件序号是多大，
// 然后跟全服邮件的序号对比，如果玩家的比较小则要读取全服邮件,否则不用
func GetUserLimit(uid int64) int64 {
	var value int64
	o := NewDBOrm(DBChess)
	sql := fmt.Sprintf(`SELECT efid FROM %v WHERE uid = %v LIMIT 1`, TableClientUser, uid)
	err := o.Raw(sql).QueryRow(&value)
	if err != nil {
		LogClient.Error(fmt.Sprintf("获取玩家全服邮件efid失败:%v\n", err))
		return 0
	}
	return value
}

//更新用户限制值
func UpdateUserLimit(uid int64, efid int64) {
	o := NewDBOrm(DBChess)
	sql := fmt.Sprintf(`UPDATE %v SET efid = %v WHERE uid = %v`, TableClientUser, efid, uid)
	_, err := o.Raw(sql).Exec()
	if err != nil {
		LogClient.Error(fmt.Sprintf("更新玩家全服邮件efid失败:%v\n", err))
	}
}

//获取玩家redis在线信息
func GetPlayerOnlineRedisInfo(uid int64, field string) int {
	key := fmt.Sprintf("%v-%v", KeyOnlineUserInfo, uid)
	l, err := Redis.HGet(key, field)
	if err != nil {
		return 0
	}
	val, _ := strconv.Atoi(l)
	return val
}

type gameBasicInfo struct {
	GameName string `json:"game_name"`
	LogoUrl  string `json:"logo_url"`
}

func GameBasicInfo(gameId int64) (*gameBasicInfo, error) {
	var gbi gameBasicInfo
	Key := fmt.Sprintf("%v%v", KeyGameInfo, gameId)
	err := GetCache(RedisCache, Key, &gbi)
	if err != nil {
		sqlGame := fmt.Sprintf(`
		SELECT
			name AS game_name, logo_url
		FROM
			%v
		WHERE
			game_id = %v
	`, TableGameList, gameId)
		o := NewDBOrm(DBChessSysData)
		err := o.Raw(sqlGame).QueryRow(&gbi)
		if CheckError(err) {
			return &gbi, err
		}
		gbi.LogoUrl = alisdk.GetOssPathUrl(OssDomain) + "/" + gbi.LogoUrl
		//即使为空也缓存，为了减少因为数据库里不存在此数据而步骤与数据库交互,所以是用checkError而不是checkNoExist
		SetCache(RedisCache, Key, gbi, CacheExpire) //一天
	}
	return &gbi, nil
}

type TableInfo struct {
	DueRound int64  `json:"dueround"`
	IsClose  int64  `json:"isclose"`
	Passwd   string `json:"passwd"`
	Round    int64  `json:"round"`
	Stime    int64  `json:"stime"`
	Etime    int64  `json:"etime"`
	User     []struct {
		Name string `json:"name"`
		//Uid  int64  `json:"uid"`
	} `json:"user"`
}

//分享房间
/*
TABLEINFO,
{"dueround":8,"isclose":false,"passwd":"470227","round":0,"user":[{"name":"娓稿.951817","uid":951817}]}
stime,etime
*/
func ShareRoom(tableId, gameId int64) (orm.Params, error) {
	var (
		status string
	)
	gbi, err := GameBasicInfo(gameId)
	if err != nil {
		return nil, err
	}

	//如果不存在就表示已解散
	key := fmt.Sprintf("%v%v", KeyTableInfo, tableId)
	if ok, _ := Redis.Exists(key); !ok {
		return map[string]interface{}{
			"game_name": gbi.GameName,
			"logo_url":  gbi.LogoUrl,
			"status":    "已解散",
		}, nil
	}

	data, err := Redis.GetString(key)
	if err != nil {
		return nil, err
	}
	var ti TableInfo
	err = json.Unmarshal([]byte(data), &ti)
	if err != nil {
		return nil, err
	}

	if ti.IsClose == 0 {
		if ti.Stime == 0 {
			status = "未开始"
		} else {
			status = "游戏中"
		}
	}

	if ti.IsClose != 0 {
		status = "已结束"
	}
	return map[string]interface{}{
		"game_name": gbi.GameName,
		"logo_url":  gbi.LogoUrl,
		"round":     ti.DueRound,
		"status":    status,
		"password":  ti.Passwd,
		"player":    ti.User,
		"number":    len(ti.User),
	}, nil
}

//TODO:做缓存
func ShareApp() ([]orm.Params, error) {
	//此版本的APP, 1-上架，0-下架
	sql := fmt.Sprintf(`
		SELECT
			type,download_url,version
		FROM
			%v
		WHERE
			status = 1
		`, TableAppVersion)
	o := NewDBOrm(DBChessSysData)
	var app []orm.Params
	_, err := o.Raw(sql).Values(&app)
	if CheckError(err) {
		return nil, err
	}
	return app, nil
}

//新建玩家的反馈
func NewFeedback(data map[string]interface{}, multiOrm ...orm.Ormer) (int64, error) {
	o := NewOrm(multiOrm, DBChess)
	values, sql := InsertSql(TableFeedback, data)
	result, err := o.Raw(sql, values).Exec()
	if err != nil {
		return 0, err
	}
	return result.LastInsertId()
}

//新建玩家的反馈图片
func NewFeedbackUrl(data map[string]interface{}, multiOrm ...orm.Ormer) (int64, error) {
	o := NewOrm(multiOrm, DBChess)
	values, sql := InsertSql(TableFeedbackUrl, data)
	result, err := o.Raw(sql, values).Exec()
	if err != nil {
		return 0, err
	}
	return result.LastInsertId()
}

//新建玩家手机记录
func NewPhoneLog(data map[string]interface{}, multiOrm ...orm.Ormer) (int64, error) {
	o := NewOrm(multiOrm, DBChess)
	values, sql := InsertSql(TablePhoneLog, data)
	result, err := o.Raw(sql, values).Exec()
	if err != nil {
		return 0, err
	}
	return result.LastInsertId()
}

//判断手机曾经是否被绑定过
func IsPhoneBindEver(phone string, multiOrm ...orm.Ormer) (PhoneLog, bool) {
	var pl PhoneLog
	sql := fmt.Sprintf(`
		SELECT
			*
		FROM
			%v
		WHERE
			phone = ?
		LIMIT 1
		`, TablePhoneLog)
	o := NewOrm(multiOrm)
	err := o.Raw(sql, phone).QueryRow(&pl)
	if CheckNoExist(err) {
		return pl, false
	}
	return pl, true
}
